Tiny FORTRAN FORM ――――――――――――――――――――――――――――――――――――――― I/O 1980年 5、6、7、8、9月号掲載 MZ−80 マシン語 起動方法 モニタからロード ――――――――――――――――――――――――――――――――――――――― [4]ステートメントの展開 皆さん、FORMを使用してみていかがでしたか? 今、私たちのもとにFORMに関しての 手紙や電話がたくさん届いています。 貴重な意見をありがとうございました。 さて、先月号では『式の展開』について、述べたので、 今月はそれ以外の『ステートメントの展開』について解説していきます。 ――――――――――――――――――― ステートメントのコンパイル ――――――――――――――――――― 1)GOTO文 FORMの構造については、5月号ですでに述べた通り、 単純な1パス型となっています。 GOTO文の展開は、 まず、この1パス型であるということを頭に入れて、 考えなければいけません。 実際的な展開は非常に単純で、 単に文番号を物理的なメモリ、アドレスに変換し、 そのアドレスにジャンプするよう JMP命令を置くだけです。 GOTO文で問題となるのは、 アドレスをいかに決定するか、ということです。 次の簡単なプログラムを見てください。 ・―――――――――――――――――・ |   A=0            | |  10 A=A+1           | |   GOTO 10          | |   END            | ・―――――――――――――――――・ このプログラムで、 文番号10はそこに飛ぶべきGOTO文よりも前にあるので、 この文を展開したときの先頭アドレスと文番号のテーブルを作って、 そこにしまいます。 こうしておくことによって、 次に出てきたGOTO文は、飛び先きの文番号によって、 先ほど作ったテーブルをサーチすれば、苦もなくアドレスの生成ができます。 これは、GOTO文よりも前に対象となる文番号が存在する場合に有効です。 しかし、次のようなプログラムの場合はどうでしょう。 ・―――――――――――――――――・ |   GOTO 10          | |   A=1            | |  10 B=1            | |   END            | ・―――――――――――――――――・ このようなプログラムでは、GOTO文の方が対象文番号よりも先に 出現してしまい、先はどのテーブルは意味を持たず、 アドレスは未定になってしまいます。 普通、このような場合には2パス型を採用し、 2度のコンパイルによって文番号のアドレスをすべてテーブルにしまっておき、 これによってGOTO文などのアドレス生成を行ないます。 FORMでは先にも述べた通り、1パス型を採用しているため、 アドレス・テーブルを作ってから再度コンパイルという方法は取れません。 では、どのようにして文番号を処理しているのでしょうか。 FORMではまず文番号に関して2つのテーブルを用意します。 1つは、先ほど述べた、すでに出現している文番号とそのアドレス用で、 他の1つは、まだ出現していない文番号を指す、ポインタ・テーブルです。 前者のテーブルの管理は簡単に行なえますが、 後者は少し、やっかいなテーブルです。 これの理解を助けるために、図1をよく見てください。   図1 文番号管理テーブルの動作 ・―――――――――――――――――――――――――――・ |  GOTO 1000                   | |    ↓                       | |  ADRS OP                    | |   2C00 C3(E8 03) → 文番号が出現していないので | |    :   ↓     アドレス 不定。        | |    : テーブルBに アドレス の代りに       | |    : ロケーション 文番号を入れておく。    | |    : を記憶                  | |    :                      | |    : B→2C01                 | |    :                      | |    ↓                      | | 1000 STOP  ―――→  文番号1000が出現した。   | |    ↓        テーブルBで1000を     | |  ADRS OP      記憶しているアドレスを探し、| |    2C83 21nnnn    そのアドレスに1000番の   | |    2C86 CDnnnn    アドレスを与える。     | |             テーブルAに文番号と    | |             アドレスを記憶させる。   | |             テーブルBでアドレスを   | |             与えたテーブルを消す。   | ・―――――――――――――――――――――――――――・ GOTO文が出現すると、 まず現在までに出現した文番号テーブルAをサーチし、 目的文番号のアドレスを捜します。 その結果、目的の文番号のアドレスがみつかれば、 そのアドレスに展開して、終了します。 目的文番号がみつからない場今は、とりあえず文番号そのままを展開し、 その展開したアドレスとまだ出現していない文番号を、 一時的にテーブルBへ記憶させます。 コンパイルを続け、 目的文番号が出現すると物理的アドレスが決定されるので、 まず、テーブルBから生成すべきアドレスを捜しだし、 そのアドレスに、本来入るべきアドレスを展開します。 次に、テーブルAへ決定されたアドレスと文番号を転送し、 テーブルBからそれを抹消します。 こうしておくことによって 1パス型でもアドレス生成か充分に行なえることが理解できることと思います。 2)IF文 IF文は( )の中の条件によって飛び先を変える文なので、 基本的には式評価とGOTO文を合わせたものと考えられます。 IF文は( )の式を展開し、その結果で擬似的にフラグをセットします。 ・―――――――――――――・ |   |式の展開|    | |   ・――――・    | |   JP M,nnnnH     | |   JP Z,nnnnH     | |   JP nnnnH      | ・―――――――――――――・ 式の展開後、条件ジャンプ命令を展開します。 nnnnHの飛び先アドレス生成の方法はまったくGOTO文と同じで、 事実、それと同じルーチンを使用しています。 IF文の展開は、この算術型IFしかないので、 比較的簡単に行なえます。 もっとも、BASICのようなIF−THEN構文も、 そう複雑な処理は必要ないように思えますが………。 3)CALL文 CALL文は、展開されたマシン・コードのレベルでも CALLとして存在します。 ・―――――――――――――――――・ |ソース →CALL 100          | |   ↓             | | マシン・コード→CALL nnnnH……CD nnnn| ・―――――――――――――――――・ これの処理はいたって簡単で、 基本的にGOTO文と何ら変わるところがありません。 つまり、FORMのCALL文は、 BASICの GOSUBとまったく同じで、 参照サブルーチンは文番号で与えられるからです。 したがって、GOTO文と違うところは 展開されたときマシン・コードのみです。 ・―――――――――――――――――・ |  GOTO文→JP nnnn→C3 nnnn  | |  CALL文→CALL nnnn→CD nnnn  | ・―――――――――――――――――・ 4)RETURN RETURNはCALL文で呼ばれたサブルーチンの最後に書き、 メイン・ルーチンに復帰するときに使用します。 RETURNを展開すると、次のようになります。 ・―――――――――――――――――・ |  RETURN            | |   ↓             | |   RET → C9H         | ・―――――――――――――――――・ RETURNの展開されたコードはC9です。 これは、CALL文を展開したときにCALL(CD)で、 サブルーチンを呼んでいますから、メインヘ戻るときにはC9のみでいいからです。 RETURNは単にC9を展開するだけで、特別な処理は何も行なっていません。 CALLで呼ばれていないのにRETURN文を実行すると どうなるのでしょうか。 私たちの実験では、完全に暴走しました。 気をつけてください。 5)DOループ FORMでは、DOループのネスティングを6レベルまでしか許していません。 DOループを展開するためには DO文と端末文とを考慮しなければなりません。 まずDO文について見てみましょう。 DO文の展開は単なるパラメータのセットのみに終わっています。 次のDO文を展開してみましょう。 ・―――――――――――――――――・ |  DO 10 I=10,100,2       | |     ↓           | |  LD HL,10D         | |  LD (VARI),HL        | |  LD HL,100D         | |  LD (DP1),HL        | |  LD HL,2D          | |  LD (DP2),HL        | ・―――――――――――――――――・ DO文の処理はパラメータを各テーブルに記憶させることしか行ないません。 DP1 は終値パラメータ・テーブル、 DP2 は増分パラメータ・テーブルで、 増分パラメータが省略されたときは、1をDP2 にセットするように 展開されます。 DO文は必ず端末文と対で使用しますが、 多くのDO文が同じ端末文を指定できるようになっています。 DOの端末文は文番号によって指定されるので、 端末文を捜し出す手法はGOTO文と同じです。 DOループの中で一番重要なのが端末文の処理で、 ここのところはよく考えないといけません。 端末文は基本的に、次のようなことを行ないます。 ループ変数に増分パラメータを加え、終値パラメータと比較します。 この結果、ループの終わりであればループを終了し、 終わりでなければループの中を再実行します。 この端末文を展開すると次のようになります。 ・―――――――――――――――――・ |   DO 10 I=1,10,1       | |  10 CONTINUE          | |     ↓            | |   LD HL,(VARI)       | |   LD BC,(DP2)        | |   LD DE,(DP1)        | |   ADD HL,BC       | |   LD (VARI),HL       | |   XOR            | |   EX DE,HL         | |   SBC HL,DE         | |   JP C,ENTRY         | ・―――――――――――――――――・ ENTRY はDO文の次の文を指しています。 6)CONTINUE文 この文は実行文の中に含まれますか、 何も実行しない文として扱われ、 DO、GOTO、CALLの飛び先として使用されるダミーです。 この文は、DOの端末文としてよく使用されます。 この文はコンパイルしても何のオブジェクトも生成しませんから、 プログラム中、どこにあっても影響はありません。 この文は文番号がついていて、 参照されるときだけテーブルにアドレスのみを記憶させます。 7)BREAK文 FORMはオブジェクト・コードに展開されてしまうと、 完全にマシン語になってしまいます。 ですから、BASICのように実行途中で止めることはできません。 したがって、途中で止めることがあらかじめ予想されるような場合に、 BREAK文 を入れておき、[BREAK]キー をチェックします。 このBREAK を展開すると次のようになります。 ・―――――――――――――――――・ |  BREAK             | |   ↓             | |  CALL BRKSUB          | ・―――――――――――――――――・ BRKSUBは[BERAK]キー のみをチェックするためのサブルーチンで、 このキーが押されている場合のみ、メッセージを出力し、 プログラムを強制的に終了させます。 キーが押されていなければリターンします。 8)RESG、SETG文 MZ−80は擬似グラフィック・キャラクタを使用して、 80×50のグラフィックが表示できます。 このグラフィック・キャラクタは1キャラクタを4分割して 表わしています(図2)。 16進数のFnで、nの4ビットはそれぞれ各ビットに対応しています(図3)。 1ドットを画面に書く場合、そのキャラクタとORを取って、 その結果に対応するキャラクタを同じ位置へ戻します。 この文を展開すると次のようになります。 ・―――――――――――――――――・ |  RESG ̄|(式1,式2)      | |  SETG_|(A,B)         | |   ↓             | |  LD HL,(VARA)| or 式1    | |  EX DE,HL           | |  LD HL,(VARB)| or 式2    | |  LD D,L            | |  EX DE,HL           | |   | ̄RESSUB         | |  CALL|          | |   |_SETSUB         | ・―――――――――――――――――・ RESSUB、SETSUBではHとLにドットのポジションが入っていますから、 このサブルーチンでは先ほど述べた通りの処理を行ないます。 図2 擬似グラフィック・キャラクタとコードの関係 ・―――――――――――――――――・ |F0→□□  F1→■□  F2→□■ | |  □□    □□    □□ | |                 | |F3→■■  F4→□□  F5→■□ | |  □□    ■□    ■□ | |                 | |F6→□■  F7→■■  F8→□□ | |  ■□    ■□    □■ | |                 | |F9→■□  FA→□■  FC→■■ | |  □■    □■    □■ | |                 | |FC→□□  FD→■□  FE→□■ | |  ■■    ■■    ■■ | |                 | |FF→■■             | |  ■■             | |注)16進数は、ディスプレイ・コードです。| ・―――――――――――――――――・ 図3 グラフィック・パターンと各ピット位置の関係 ・―――――――――――――――――・ |       ___       | |  ビット1→| | |←ビット2  | |      |―+―|      | |  ビット3→| | |←ビット4  | |        ̄ ̄ ̄       | ・―――――――――――――――――・ 9)WRITE文 FORMは標準FORTRANと違い、FORMAT文は存在しません。 そのため、WRITE文 にFORMAT的要素を含める形式で、 WRITE文 を記述するようになっています。 FORMは16ビットの整数しか扱えないので、 FORMATTINGする数は多くありません。 FORMATTINGするのは、 ・―――――――――――――――――・ |10進、16進、CHR、TAB、CR、文字列、カーソル | ・―――――――――――――――――・ の7つです。 これの記述形式は次のように定義します。 ・―――――――――――――――――・ |  EXP.In 10進          | |  EXP.Bn 16進          | |  EXP.An CHR          | |  EXP.X TAB          | |  EXP.V カーソル・バーチカル      | |  EXP.H カーソル・ホリゾンタル      | |  / CR           | |  "~" 文字列         | ・―――――――――――――――――・ WRITE文中、 これらの要素は複数個記述することができ、 それぞれのセパレータは『,』です。 nは桁数ですが、Bは2または4、 Aは1または2の数値しか使用できません。 これを展開すると次のようになります。 ・―――――――――――――――――・ |(1)10進             | |    A.I10           | |     ↓           | |   LD HL,(VARA)       | |   LD A,0AH         | |   CALL DECMAL         | |                 | |(1)16進             | |    32767.B4         | |     ↓           | |   LD HL,7FFFH        | |   LD A,4H          | |   CALL HEXPRT         | |                 | |(3)CHR            | |    1.A1           | |     ↓           | |   LD HL,1H         | |   LD A,1H          | |   CALL CHRPRT         | |                 | |(4)TAB            | |    20.X           | |     ↓           | |   LD HL14H         | |   CALL TABSUB         | |                 | |(5)カーソル|V|        | |      |H|        | |    10.|H|          | |     |V|          | |     ↓           | |    LD HL,0AH        | |    CALL CERSLH or CERSLV   | |                 | |(6)CR             | |     /            | |     ↓           | |    CALL CROUT        | |                 | |(7)文字列            | |   "ABCD"           | |     ↓           | |   CALL STRPRT         | |   DEFM 'ABCD'         | |   DEFB 0           | ・―――――――――――――――――・ WRITE文 は“WRITE” そのものを展開せず、 FORMAT的部分を展開することによって、その処理を終了します。 10)READ文 READ文はWRITE文 とまったく同じ処理を行ないます。 FORMのREAD文はBASICのINPUT文 と同じように、 文字列の出力が可能となっています。 READのFORMATTINGの形式は次のように定義してあります。 この形式はCR、文字列出力を含め、5つの形式があります。 ・―――――――――――――――――・ |  VAR.I 10進入力        | |  VAR.B 16進入力        | |  VAR.An 文字入力、nは1or2   | ・―――――――――――――――――・ CR、文字列出力は、WRITE文と同じルーチンを共用しています。 ・―――――――――――――――――・ |(1)10進入力           | |   A.I             | |   ↓             | |  CALL DECINP          | |  LD (VARA),HL        | |                 | |(2)16進入力           | |   A.B             | |   ↓             | |  CALL HEXINP          | |  LD (VARA),HL        | |                 | |(3)文字入力           | |   B.A2            | |   ↓             | |  LD A,02H          | |  CALL CHRINP          | |  LD (VARB),HL        | |                 | ・―――――――――――――――――・ 11)STOP文 STOP文はプログラムの終了を表わす文で、 実行文の中に含まれ、プログラム中どこにあってもかまいません。 プログラムがSTOP文で終了すると ・―――――――――――――――――・ |  STOP 1 2 3          | |     ̄ ̄ ̄          | |     10進          | ・―――――――――――――――――・ と出力され,これを展開すると次のようになります。 ・―――――――――――――――――・ |  STOP 123           | |     ̄ ̄           | |   ↓  ↓           | |  LD HL,7BH          | |  CALL STOPSB          | ・―――――――――――――――――・ STOPの後に10進数が出力されるのは FORTRANのSTOPと同じようにしたためです。 ―――――――――――――――――――      最 後 に ――――――――――――――――――― ここまで読んで感じていると思いますが、 各ステートメントの展開は非常に単純に行なわれています。 ステートメント処理のほとんどはサブルーチンに押しつけてしまい、 コンパイルのスピードを上げています。